package GA;

import instrumentation.TrackerCoverage;

import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;

import Operations.ValuesGenerator;
import mutation.Mutation;
import crossover.Crossover;
import random.TestClusterBuilder;
import util.*;
import selection.*;

public class Population {
	private static final int POPULATION_SIZE    = GAConfig.PopulationSize;
	public static Chromosome[] randomPopulationInitializer(TestClusterBuilder TestCluster) {		
		Chromosome[] population = new Chromosome[POPULATION_SIZE];
		//Generate random population
		for(int individual = 0; individual < POPULATION_SIZE; individual++) {
			population[individual]=ChromosomeFormer.buildNewChromosome(TestCluster);
		}
		
		return population;
	}
	public static Chromosome[] generateNewPopulation(GAInfo gaInfo,TestClusterBuilder TestCluster) {
        Selection parentSelector = GAOperationsConfig.getParentSelector();
        Crossover crossoverStrategy = GAOperationsConfig.getCrossoverStrategy();
        parentSelector.setExtraInfo(gaInfo);
        Chromosome[] newPopulation = new Chromosome[POPULATION_SIZE];
        Chromosome[] population = gaInfo.getPopulation();
        int elitismNum=(int)(((double)GAConfig.PopulationSize/(double)100) * GAConfig.elitismNumBest); 
        for (int j=elitismNum; j < POPULATION_SIZE ;) {
                int parent_Indx_1=parentSelector.selectTwoParents();
                int parent_Indx_2=parentSelector.selectTwoParents();
 //              System.out.println("parent_Indx_1: "+ parent_Indx_1+ " parent_Indx_2: "+ parent_Indx_2+"\n"); 
                Chromosome parent1=gaInfo.getPopulation()[parent_Indx_1].clone();
                Chromosome parent2=gaInfo.getPopulation()[parent_Indx_2].clone();
//                System.out.println("parent1: "+parent1.toString());
//                System.out.println("parent2: "+parent2.toString());
                //Get the two children
                Chromosome[] offspring = crossoverStrategy.crossOver(parent1, parent2);

                if(GAConfig.generateNewPopulation)System.out.println("offspring1: "+offspring[0].toString());
                if(GAConfig.generateNewPopulation)System.out.println("offspring2: "+offspring[1].toString());
 
                mutateChromosome(offspring[0],gaInfo);
                mutateChromosome(offspring[1],gaInfo);
                
                
                newPopulation[j++]=offspring[0];
                if(j<POPULATION_SIZE)
                	newPopulation[j++]=offspring[1];
        }
        //Order initial population based on fitness
//		Population.toString(gaInfo);
        Arrays.sort(population);
//		Population.toString(gaInfo);
        for(int i=0; i<elitismNum;i++){
                if(GAConfig.generateNewPopulation)System.out.println("elitismNumBest" + i +": "+population[i].toString());
                newPopulation[i]= population[i];//.clone();
        }

        return newPopulation;
        
	}
	private static void mutateChromosome(Chromosome offspring,GAInfo gaInfo) {
		Mutation ChangeValueStrategy = GAOperationsConfig.getMutationChangeValueStrategy();
		Mutation MethodInsertStrategy = GAOperationsConfig.getMutationMethodInsertStrategy();
		Mutation MethodDeleteStrategy = GAOperationsConfig.getMutationMethodDeleteStrategy();
		ChangeValueStrategy.setExtraInfo(gaInfo);
		MethodInsertStrategy.setExtraInfo(gaInfo);
		MethodDeleteStrategy.setExtraInfo(gaInfo);
		try {
			int ran = ValuesGenerator.getRandomIntBetween(1,90);
			if (ran <= 30) {
//				System.err.println(" changeInputValue()");
				ChangeValueStrategy.mutateChromosome(offspring);
			} else if (ran <= 60) {
//				System.err.println(" insert Related/Random Method Call()");
				MethodInsertStrategy.mutateChromosome(offspring.clone());
			}else if (ran <= 90) {
//				System.err.println(" Delete Related/Random Method Call()");
				MethodDeleteStrategy.mutateChromosome(offspring);
			}
		} catch (Throwable e) {
			System.err.println("Population.mutateChromosome: " + e);
			System.exit(1);
		}

	}

	public static void toString(GAInfo gaInfo) {
		System.out.println("\t\t Generation "+ gaInfo.getGeneration() +"\n");
		for(Chromosome individual : gaInfo.getPopulation()) {
			System.out.println( " size: "+ individual.getSize() + " fitness: "+ individual.getFitness());
			//System.out.println( " size: "+ individual.toString() + " fitness: "+ individual.getFitness());
		}
	}
	public static void main(String[] args) {
		GAInfo     gaInfo     = new GAInfo();
		  TrackerCoverage track=  TrackerCoverage.getTracker();
			 Set<Class<?>> allClasses = new LinkedHashSet<Class<?>>();
			ValuesGenerator.resetSeed(20);
			System.out.println("CUT is:" + GAConfig.classFilePath);			
		      allClasses.addAll(TestClusterBuilder.readClassFromFile());
		      Set<Class<?>> classesToTest = TestClusterBuilder.filterUntestableClasses(allClasses);
		      
				// calculate the test cluster and the set of types involved in the
				TestClusterBuilder TestCluster = new TestClusterBuilder(classesToTest);		
				Chromosome[] population = Population.randomPopulationInitializer(TestCluster);
				gaInfo.setPopulation(population);
				Population.toString(gaInfo);
				
		System.exit(1);
	}
}
